﻿/*
 * Created by SharpDevelop.
 * User: phtrung
 * Date: 6/29/2009
 * Time: 2:41 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Collections;
using popLib;


namespace QuickCss2Sprite
{
	class Program
	{
        static string srcDir = "";
        static string desDir = "_copy.png";
		static bool   cleanUpTemp = true;
        static string file_config = FileLib.getFullPath("config.ini");

        static int limitWidth = 1000; 	//litmit with of final image(maybe wider a bit)
        static int initWidth = 5000;  	//init width of background image
        static int initHeight = 5000; 	//init height of background image		
        static int currentX = 0;		//X postion of the pointer
        static int currentY = 0;		//Y postion of the pointer        
        static int maxHeight = 0;		//max height of current image line(for add next line later)
        static int actualWidth = 0;		//actual width of the final image(to crop later)
        static int actualHeight = 0;	//actual height of the final image(to crop later)
        static Image bg;				//background image(bg)
        static Graphics myGraphic;		//graphic to draw into background(bg)

		public static void Main(string[] args)
		{
            Profile config = ProfileLib.initProfile(ProfileLib.ProfileType.Ini, file_config);

            srcDir = config.getValue("Information", "SourcePath", "");
            desDir = config.getValue("Information", "DestinationFile", "spriteCSS.png");
            cleanUpTemp = config.getBoolean("Information", "LeaveTempDirectory", false);

            limitWidth = config.getInt("Setting", "LimitWidth", 1000);


            makeSpriteImage(srcDir);

            //Console.WriteLine("QuickCss2Sprites /S source [/D destination] [/L]");
            //Console.WriteLine("/S source\t\tIndicate image source address.");				
            //Console.WriteLine("/D destination\t\tIndicate image output(by default the output file in same folder with execute file - \"_copy.png\" )\r\n");
            //Console.WriteLine("/L leave all created pieces of COMPONENT IMAGE.");
			
            //Console.WriteLine("Note: work with PNG, JPG, GIF image only. PNG file is recommended.");
            //Console.WriteLine("Pattern name for COMPONENT IMAGE is: __NAME(##).***");
            //Console.WriteLine("\tNAME: name of component image.");
            //Console.WriteLine("\t##: width and height of a corner.");
            //Console.WriteLine("\t***: image extension(PNG; JPG; GIF).");
            //Console.WriteLine("Enter to exist.");
            //Console.Read();
		}
			
		
		//START FUNCTION HERE
		//make css sprite for all images in selected path
		public static void makeSpriteImage(string path){
			initImage();
			initDestination();
						
			picture[] picArr = getAllImageAttribute(getImageList(path));
			addArrayPic(picArr, false);
			
			if(create4GroupImage(path)){
				picArr = getAllImageAttribute(getImageList(cornerDir));
				addArrayPic(picArr, true);
				
				picArr = getAllImageAttribute(getImageList(sideDir));
				addArrayPic4Side(picArr);
			}			
			
			buildSpriteImage();
			
			cleanUpTempFolder();
		}
		
		//declare bg and graphic object for later use
		public static void initImage(){
			bg = createImage( initWidth , initHeight);
			myGraphic = Graphics.FromImage(bg);
		}		
		
		static string destinationPath = "";
        static string destinationFile = desDir;		
		static ImageFormat destinationFormat = ImageFormat.Png;
		//get destination and then update file destination info
		public static void initDestination(){
            if (desDir.IndexOf("/") >= 0)
            {
                destinationFile = desDir.Split('/')[desDir.Split('/').Length - 1];
                destinationPath = desDir.Replace("/" + destinationFile, "");
				if( !Directory.Exists(destinationPath) ) Directory.CreateDirectory(destinationPath);
            }
            else if (desDir.IndexOf("\\") >= 0)
            {
                destinationFile = desDir.Split('\\')[desDir.Split('\\').Length - 1];
                destinationPath = desDir.Replace("\\" + destinationFile, "");
				if( !Directory.Exists(destinationPath) ) Directory.CreateDirectory(destinationPath);
            }
            else destinationFile = desDir;
			
			if(destinationFile.ToLower().LastIndexOf(".gif") > 0) destinationFormat = ImageFormat.Gif;
			if(destinationFile.ToLower().LastIndexOf(".jpg") > 0) destinationFormat = ImageFormat.Jpeg;			
			
		}
		
		//END FUNCTION HERE
		//complete the process by crop the actual size and save image
		public static void buildSpriteImage(){
			myGraphic.Save();			
			bg = cropImage( bg, 0, 0, actualWidth, actualHeight);

            if (desDir.ToLower().IndexOf(".gif") > 0) bg = SaveGIFWithNewColorTable((Image)bg, 256, true);
            bg.Save(desDir, destinationFormat);
			
			Bitmap spacer = new Bitmap(1,1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed );
			spacer = SaveGIFWithNewColorTable( (Image) spacer, 256, true);
			spacer.Save( destinationPath.Length>0 ? destinationPath + "/spacer.gif" : "spacer.gif", ImageFormat.Gif );			
			buildCss();
		}		
		
		//add image one by one to background(bg) image, with passed picture array
		public static void addArrayPic(picture[] picArr, bool includeSideImage){
			bool isAccepted = includeSideImage;
			foreach( picture pic in picArr){
				Image tmp = Image.FromFile(pic.Path);			
				includeSideImage = isAccepted;
					
				if(!includeSideImage && !pic.IsSideImage) includeSideImage = true;				
					
				if( !pic.IsGroupImage && includeSideImage ){
					myGraphic.DrawImage(tmp, currentX, currentY, pic.Width, pic.Height);
					addCss(pic, currentX, currentY);				
					
					currentX+= pic.Width;
					if(maxHeight<pic.Height) maxHeight = pic.Height;
					
					if(actualWidth < currentX) actualWidth = currentX;
					
					if( currentX > limitWidth ){
						currentY +=maxHeight;
						maxHeight = currentX = 0;
					}
					
					if(actualHeight < currentY + pic.Height) actualHeight = currentY + pic.Height;				
				}
				tmp.Dispose();
			}
		}
		//add image 4 side for group image
		public static void addArrayPic4Side(picture[] picArr){		
			
			
			//to solve the problem of blur image when you spread them out.
			myGraphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
			
			//add for top and bottom side images first
			currentX = 0;
			currentY = actualHeight;			
			string topbottomName = "_1s;_4s;~";
			foreach( picture pic in picArr){
				if( indexOfAny( pic.FileName, topbottomName ) >= 0 ){
					Image tmp = Image.FromFile(pic.Path);
					
					myGraphic.DrawImage(tmp, currentX, currentY, limitWidth*2, pic.Height);
					pic.FileName = pic.FileName.Replace("~","");
					
					addCss(pic, currentX, currentY);
					
					actualHeight += pic.Height;					
					currentY	 += pic.Height;
					
					tmp.Dispose();
				}
			}
			
			//add for left and right side images then
			//if(the actualWidth is too short change to 1000px)
			if(actualWidth < 1000) actualWidth = 1000;
			currentX = actualWidth;
			currentY = 0;
			string leftrightName = "_2s;_3s;!";
			foreach( picture pic in picArr){
				if( indexOfAny( pic.FileName, leftrightName ) >= 0){
					Image tmp = Image.FromFile(pic.Path);
					
					myGraphic.DrawImage(tmp, currentX, currentY, pic.Width, actualHeight*2);					
					pic.FileName = pic.FileName.Replace("!","");
					addCss(pic, currentX, currentY);
					
					actualWidth += pic.Width;
					currentX	+= pic.Width;
				
					tmp.Dispose();
				}
			}
		}
		
		
		//Make GroupImage***********************************************		
		//for image to make a panel
		//crop it to 2 pieces: 1 corner + 1 side 
		//and then rotate + make copy 3 time more
		//the passed parameter is selected path > search all file with name like "__NAME(##).***"
		//NAME: name of the image; ##: width and height of a corner(must be number)	
		//return value is an array object include 2 array pictures( corner and side picture array )
		static string tempDir   = "_GroupImageTemp_";
		static string cornerDir = tempDir + "/Corner";
		static string sideDir   = tempDir + "/Side";
		public static bool create4GroupImage(string path){
			bool hasGroupImage = false;
			
			picture[] picArr = getAllImageAttribute(getFileListExt( path, "*.png;*.gif;*.jpg;" ));
			if(Directory.Exists(tempDir))Directory.Delete(tempDir, true);
			else Directory.CreateDirectory(tempDir);
			
			Directory.CreateDirectory(cornerDir);
			Directory.CreateDirectory(sideDir);
						
			foreach( picture pic in picArr ){
				if(pic.IsGroupImage){
					hasGroupImage = true;
										
					Image img_part = createImage(pic.MaxSize, pic.MaxSize);
					
					//create 4 corners
					Graphics graphics = Graphics.FromImage( img_part );
					Image img_tmp = Image.FromFile(pic.Path);
					graphics.DrawImage(img_tmp, 0, 0);
					graphics.Save();					
					
					Image tmp = img_part;
					
					img_part.Save( cornerDir + "/" + pic.FileName + "_1.png", ImageFormat.Png );
					img_part.RotateFlip( RotateFlipType.Rotate180FlipY);
					img_part.Save( cornerDir + "/" + pic.FileName + "_2.png", ImageFormat.Png );
					img_part.RotateFlip( RotateFlipType.Rotate90FlipX);
					img_part.Save( cornerDir + "/" + pic.FileName + "_3.png", ImageFormat.Png );
					img_part.RotateFlip( RotateFlipType.Rotate180FlipY);					
					img_part.Save( cornerDir + "/" + pic.FileName + "_4.png", ImageFormat.Png );
					
					//create 4 sides
					img_part = createImage(1, pic.MaxSize);
					
					graphics = Graphics.FromImage( img_part );				
					graphics.DrawImage(img_tmp, - pic.MaxSize , 0);
					graphics.Save();
					
					img_part.Save( sideDir + "/" + pic.FileName + "_1s.png", ImageFormat.Png );
					img_part.RotateFlip( RotateFlipType.Rotate90FlipY);
					img_part.Save( sideDir + "/" + pic.FileName + "_2s.png", ImageFormat.Png );
					img_part.RotateFlip( RotateFlipType.Rotate180FlipY);
					img_part.Save( sideDir + "/" + pic.FileName + "_3s.png", ImageFormat.Png );
					img_part.RotateFlip( RotateFlipType.Rotate90FlipY);
					img_part.Save( sideDir + "/" + pic.FileName + "_4s.png", ImageFormat.Png );
						
					img_part.Dispose();
				}
				//copy all part of image corner and side that you create to
				//sideDir
				else if( pic.IsSideImage ){
					hasGroupImage = true;
					File.Copy( pic.Path, sideDir + "\\" + pic.Path.Substring(pic.Path.LastIndexOf("\\")+1) );
				}
				
				
			}
			return hasGroupImage;
		}
		
		public static void cleanUpTempFolder(){
			//if(cleanUpTemp && Directory.Exists(tempDir))Directory.Delete(tempDir, true);
		}
		
		
		//BUILD CSS FOR IMAGE**************************************************************
		static string mainCss  = "[CLS]{\r\n\tbackground: url([IMG]);[ADD]\r\n}\r\n\r\n";
		static string imageCss = "[CLS]{\r\n\tbackground-position: [POX]px [POY]px;[ADD]\r\n}\r\n\r\n";
		static string image2s  = "[CLS]{\r\n\tbackground-position: [POX]px [POY]px;\r\n\tmargin-left: [LEF]px; \r\n\tbackground-repeat:repeat-y;\r\n\ttext-align:left;\r\n}\r\n\r\n";
		static string image3s  = "[CLS]{\r\n\tbackground-position: [POX]px [POY]px;\r\n\tclear:both;\r\n\tbackground-repeat:repeat-y;\r\n}\r\n\r\n";
		static string sizeCss  = "\r\n\twidth:[WID]px;\r\n\theight:[HEI]px;";		
		static string finalCss = "";		
		
		
		static string imageTag = "<img src='spacer.gif' class='[CLS]'> [FIL]([POX] [POY] [WID] [HEI])<BR><BR><BR><BR>";
		static string finalHtml= "<link rel=\"stylesheet\" type=\"text/css\" media=\"screen\" href=\"style.css\" />\r\n\r\n";
		//create css for passed image.
		public static void addCss(picture pic, int x, int y){
			Console.WriteLine(pic.FileName);
			
			//style.css*******************************************************************
			if(finalCss.Length == 0){
				finalCss = mainCss.Replace("[CLS]", "[CLS] ."+pic.FileName);
				finalCss = finalCss.Replace("[IMG]", destinationFile );
				finalCss = finalCss.Replace("[ADD]", "");
			}else
				finalCss = finalCss.Replace("[CLS]", "[CLS] ."+pic.FileName+",");				
			
			
			//search for name _2s _3s, and apply special css for them
			if(pic.FileName.IndexOf("_2s")>0){
				finalCss+= image2s.Replace("[CLS]", "."+pic.FileName).Replace("[LEF]", pic.Width+"");
				addComponentInfo(pic, x, y);
			}
			else if(pic.FileName.IndexOf("_3s")>0) finalCss+= image3s.Replace("[CLS]", "."+pic.FileName);
			else finalCss+= imageCss.Replace("[CLS]", "."+pic.FileName).Replace("[ADD]", sizeCss);
			
			finalCss = finalCss.Replace("[POX]", x > 0 ? "-"+x : x+"");
			finalCss = finalCss.Replace("[POY]", y > 0 ? "-"+y : y+"");
			finalCss = finalCss.Replace("[WID]", pic.Width+"");
			finalCss = finalCss.Replace("[HEI]", pic.Height+"");
			
			//test.html********************************************************************
			finalHtml+= imageTag.Replace("[CLS]",  pic.FileName);
			finalHtml = finalHtml.Replace("[FIL]", pic.FileName);
			finalHtml = finalHtml.Replace("[POX]", x > 0 ? "-"+x : x+"");
			finalHtml = finalHtml.Replace("[POY]", y > 0 ? "-"+y : y+"");
			finalHtml = finalHtml.Replace("[WID]", pic.Width+"");
			finalHtml = finalHtml.Replace("[HEI]", pic.Height+"");
		}
		
		
		//build the final Css file(save file)
		public static void buildCss(){
			finalCss = finalCss.Replace("[CLS] ", "");
			StreamWriter sw = new StreamWriter( destinationPath.Length>0 ? destinationPath + "/style.css" : "style.css");
			sw.Write(finalCss);
			sw.Flush();
			sw.Close();			
			
			sw = new StreamWriter(destinationPath.Length>0 ? destinationPath + "/test.html" : "test.html" );
			sw.Write(finalHtml);
			sw.Flush();
			sw.Close();
			
			buildComponentInfo();
		}
		
		static string arr2s    = "\r\n\t$arrComponent\t= array([COM]);\r\n\t$arrPosition\t= array([POS]);\r\n\t$arrWidth\t\t= array([WID]);\r\n";
		//get information of 2s border and generate array for using in PHP helper
		public static void addComponentInfo(picture pic, int x, int y){
			arr2s = arr2s.Replace( "[COM]", "[COM], \"" +  pic.FileName.Replace("_2s","\"") );
			arr2s = arr2s.Replace( "[POS]", "[POS], " + x );
			arr2s = arr2s.Replace( "[WID]", "[WID], " + pic.Width );
		}
		//save the array list to file [components.txt]
		public static void buildComponentInfo(){
			arr2s = arr2s.Replace( "[COM], ", "" );
			arr2s = arr2s.Replace( "[POS], ", "" );
			arr2s = arr2s.Replace( "[WID], ", "" );
			StreamWriter sw = new StreamWriter( destinationPath.Length>0 ? destinationPath + "/components.txt" : "components.txt" );
			sw.Write(arr2s);
			sw.Flush();
			sw.Close();
		}
		
		//**************************************************************
				
		public static picture[] getAllImageAttribute(string[] images){
			picture[] pic = new picture[images.Length];
			for(int i=0;i<images.Length;i++)
				pic[i] = getImageAttribute(images[i]);
			return pic;
		}		
				
		public static picture getImageAttribute(string image){
			System.Drawing.Image img = System.Drawing.Image.FromFile(image);
			picture tmp = new picture();
			tmp.Path = image;
			tmp.Width = img.Width;
			tmp.Height = img.Height;
			
			return tmp;
		}
		
		
		public static string[] getImageList(string path){
			string ext = "*.gif;*.png;*.jpg";
			return getFileListExt(path, ext);
		}
		
		public static string[] getFileListExt(string path, string extensionList){
			string[] extensions = extensionList.Split(';');
			ListBox 	lb_tmp = new ListBox();
			foreach(string ext in extensions)
				lb_tmp.Items.AddRange( getFileList( path, ext ) );
			
			string[] result = new string[lb_tmp.Items.Count];
			lb_tmp.Items.CopyTo(result,0);
			return result;
		}
		
		public static string[] getFileList(string path, string extension){
			return Directory.GetFiles( path, extension );
		}
		
		public static Image createImage( int nWidth, int nHeight )
		{
		  Bitmap result = new Bitmap( nWidth, nHeight );		  		 
		  return (Image)result;
		}
		
		public static Image cropImage( Image img, int x, int y, int nWidth, int nHeight )
		{
		  Bitmap result = new Bitmap( nWidth, nHeight );		  
		  
		  using( Graphics g = Graphics.FromImage( (Image) result ) )
		    g.DrawImageUnscaled( img, x, y, nWidth, nHeight );
		  return (Image)result;
		}
		
        
        //**************************************************************
		//get string from string A, to string B but dont have A and B
		//12345678 --> get from 23 to 78 --> 456
		public static string subStringBetween(string src, string from, string to, int fromIndex){
			return subString(src,from,to,fromIndex).Replace(from,"").Replace(to,"");
		}
		//get string from string A, to string B
		//12345678 --> get from 23 to 78 --> 2345678
		public static string subString(string src, string from, string to, int fromIndex){
			string result = "";		
			
			if(src.IndexOf(from,fromIndex) >= 0 ){
				int startIndex = src.IndexOf(from,fromIndex);
				int endIndex   = src.IndexOf(to, startIndex + from.Length);
				int length	   = endIndex - startIndex + to.Length;
				if(startIndex >= 0 && length > 0)
				result = src.Substring( startIndex, length);
			}
			return result;			
		}
		
		//matchAny: str1;str2;str3
		public static int indexOfAny(string name, string matchAny){
			string[] arr = matchAny.Split(';');
			foreach(string item in arr)
				if(name.IndexOf(item)>=0) return name.IndexOf(item);
			return -1;
		}
		
		public static Bitmap SaveGIFWithNewColorTable(Image image, uint nColors, bool fTransparent){
    
                // GIF codec supports  colors maximum, monochrome minimum.
                if (nColors > 256)
                    nColors = 256;
               if (nColors < 2)
                    nColors = 2;
    
               // Make a new -BPP indexed bitmap that is the same size as the source image.
               int Width = image.Width;
               int Height = image.Height;
   
               // Always use PixelFormatbppIndexed because that is the color
               // table-based interface to the GIF codec.
               Bitmap bitmap = new Bitmap(Width,
                                       Height,
                                       PixelFormat.Format8bppIndexed);
   
               // Create a color palette big enough to hold the colors you want.
               ColorPalette pal = GetColorPalette(nColors);
   
               // Initialize a new color table with entries that are determined
               // by some optimal palette-finding algorithm; for demonstration 
               // purposes, use a grayscale.
               for (uint i = 0; i < nColors; i++)
               {
                   uint Alpha = 0xFF;                      // Colors are opaque.
                   uint Intensity = i * 0xFF / (nColors - 1);    // Even distribution. 
   
                   // The GIF encoder makes the first entry in the palette
                   // that has a ZERO alpha the transparent color in the GIF.
                   // Pick the first one arbitrarily, for demonstration purposes.
   
                   if (i == 0 && fTransparent) // Make this color index...
                       Alpha = 0;          // Transparent
   
                   // Create a gray scale for demonstration purposes.
                   // Otherwise, use your favorite color reduction algorithm
                   // and an optimum palette for that algorithm generated here.
                   // For example, a color histogram, or a median cut palette.
                   pal.Entries[i] = Color.FromArgb((int)Alpha, pal.Entries[i]);
               }
   
               // Set the palette into the new Bitmap object.
               bitmap.Palette = pal;
   
   
               // Use GetPixel below to pull out the color data of Image.
               // Because GetPixel isn't defined on an Image, make a copy 
               // in a Bitmap instead. Make a new Bitmap that is the same size as the
               // image that you want to export. Or, try to
               // interpret the native pixel format of the image by using a LockBits
               // call. Use PixelFormatBppARGB so you can wrap a Graphics  
               // around it.
               Bitmap BmpCopy = new Bitmap(Width,
                                       Height,
                                       PixelFormat.Format32bppArgb);
               {
                   Graphics g = Graphics.FromImage(BmpCopy);
   
                   g.PageUnit = GraphicsUnit.Pixel;
   
                   // Transfer the Image to the Bitmap
                   g.DrawImage(image,0 ,0 , Width, Height);
   
                   // g goes out of scope and is marked for garbage collection.
                   // Force it, just to keep things clean.
                   g.Dispose();
               }
   
               // Lock a rectangular portion of the bitmap for writing.
               BitmapData bitmapData;
               Rectangle rect = new Rectangle(0, 0 , Width, Height);
   
               bitmapData = bitmap.LockBits(
                   rect,
                   ImageLockMode.WriteOnly,
                   PixelFormat.Format8bppIndexed);
   
               // Write to the temporary buffer that is provided by LockBits.
               // Copy the pixels from the source image in this loop.
               // Because you want an index, convert RGB to the appropriate
               // palette index here.
               IntPtr pixels = bitmapData.Scan0;
   
               unsafe
               {
                   // Get the pointer to the image bits.
                   // This is the unsafe operation.
                   byte* pBits;
                   if (bitmapData.Stride > 0)
                       pBits = (byte*)pixels.ToPointer();
                   else
                       // If the Stide is negative, Scan points to the last 
                       // scanline in the buffer. To normalize the loop, obtain
                       // a pointer to the front of the buffer that is located 
                       // (Height-) scanlines previous.
                       pBits = (byte*)pixels.ToPointer() + bitmapData.Stride * (Height - 1);
                  uint stride = (uint)Math.Abs(bitmapData.Stride);
  
                  for (uint row = 0; row < Height; ++row)
                  {
                      for (uint col = 0; col < Width; ++col)
                      {
                          // Map palette indexes for a gray scale.
                          // If you use some other technique to color convert,
                          // put your favorite color reduction algorithm here.
                          Color pixel;    // The source pixel.
  
                          // The destination pixel.
                          // The pointer to the color index byte of the
                          // destination; this real pointer causes this
                          // code to be considered unsafe.
                          byte* pbppPixel = pBits + row * stride + col;
  
                          pixel = BmpCopy.GetPixel((int)col, (int)row);
  
                          // Use luminance/chrominance conversion to get grayscale.
                          // Basically, turn the image into black and white TV.
                          // Do not calculate Cr or Cb because you 
                          // discard the color anyway.
                          // Y = Red * . + Green * . + Blue * .
  
                          // This expression is best as integer math for performance,
                          // however, because GetPixel listed earlier is the slowest 
                          // part of this loop, the expression is left as 
                          // floating point for clarity.
  
                          double luminance = (pixel.R * 0.299) +
                              (pixel.G * 0.587) +
                              (pixel.B * 0.114);
  
                          // Gray scale is an intensity map from black to white.
                          // Compute the index to the grayscale entry that
                          // approximates the luminance, and then round the index.
                          // Also, constrain the index choices by the number of
                          // colors to do, and then set that pixel's index to the 
                          // byte value.
                          *pbppPixel = (byte)(luminance * (nColors - 1) / 255 + 0.5);
  
                      } /* end loop for col */
                  } /* end loop for row */
              } /* end unsafe */
  
              // To commit the changes, unlock the portion of the bitmap.  
              bitmap.UnlockBits(bitmapData);
  
              //// Bitmap goes out of scope here and is also marked for
              //// garbage collection.
              //// Pal is referenced by bitmap and goes away.
              //// BmpCopy goes out of scope here and is marked for garbage
              //// collection. Force it, because it is probably quite large.
              //// The same applies to bitmap.
              //BmpCopy.Dispose();
              //bitmap.Dispose();
  
              return bitmap;
          }
		
		
		public static ColorPalette GetColorPalette(uint nColors)
		{
			// Assume monochrome image.
			PixelFormat bitscolordepth = PixelFormat.Format1bppIndexed;
			ColorPalette palette;    // The Palette we are stealing
			Bitmap bitmap;     // The source of the stolen palette
			
			// Determine number of colors.
			if (nColors > 2)
			  bitscolordepth = PixelFormat.Format4bppIndexed;
			if (nColors > 16)
			  bitscolordepth = PixelFormat.Format8bppIndexed;
			
			// Make a new Bitmap object to get its Palette.
			bitmap = new Bitmap(1, 1, bitscolordepth);
			
			palette = bitmap.Palette;   // Grab the palette
			
			bitmap.Dispose();           // cleanup the source Bitmap
			
			return palette;             // Send the palette back
		}
	}
	
	//group image: __NAME(MAXSIZE).***
	class picture{
		string filename;
		bool isGroupImage = false;
		bool isSideImage = false;		
		int maxSize;
		string path;
		int width;
		int height;
		
		
		//retur filename without extension
		public string FileName{
			get{return filename;}
			set{filename = value;}
		}		
		
		public bool IsGroupImage{
			get{return isGroupImage;}			
		}
		public bool IsSideImage{
			get{return isSideImage;}			
		}		
		
		public int MaxSize{
			get{return maxSize;}			
		}
		
		public int Width{
			get{return width;}
			set{width = value;}
		}
		
		public int Height{
			get{return height;}
			set{height = value;}
		}
		
		public string Path{
			get{return path;}
			set{
				path = value;
				
				//get file name
				filename = path;
				
				if( path.IndexOf("/") >= 0 ) filename = path.Split('/')[path.Split('/').Length-1];
				if( path.IndexOf("\\") >= 0 ) filename = path.Split('\\')[path.Split('\\').Length-1];
				filename = filename.Replace(" ", "_").Substring(0, filename.Length-4);
				
				string sideImageName = "_1s.;_2s.;_3s.;_4s.;";
				string sideImageName1 = "~;!";
				
				if(filename.IndexOf("__") == 0 && filename.IndexOf("(") > 0 && filename.IndexOf(")") > 0){
					isGroupImage = true;
					maxSize = Convert.ToInt16( Program.subStringBetween( filename, "(", ")", 0 ) );
					filename = Program.subStringBetween( filename, "__", "(", 0 );
				}else if( Program.indexOfAny( filename, sideImageName ) > 0 ){
					isSideImage = true;
				}else if(Program.indexOfAny( filename, sideImageName1 ) == 0){
					isSideImage = true;
				}
				
				
					
			}
		}
	}
}
